#include <stdio.h>
#include <assert.h>
#include <winsock2.h>
#include <windows.h>

DWORD WINAPI threadTcpRealIO(LPVOID para) {
	// para : read, write socket handle
	while (1) {
		char buf[128];
		int recvSize = recv(para, buf, 128, 0); // block it now

		if (recvSize == 0 || recvSize == SOCKET_ERROR) {
			printf("socket handle: %p recv return 0 or return SOCKET_ERROR, and go out\n\n", para);
			break;
		}

		printf("socket handle: %p recv %d bytes\n", para, recvSize);
		send(para, buf, recvSize, 0);

		if (buf[0] == 'q') {
			printf("socket handle: %p go out\n\n", para);
			closesocket(para);			
			break;
		}
	}
	return 0;
}

DWORD WINAPI threadTcpListenning(LPVOID para) {
	SOCKET new_skt;
	struct sockaddr_in addr_other;
	int addr_other_size;
	static int i;

	while (1) {
		HANDLE h1;
		addr_other_size = sizeof(struct sockaddr_in);
		new_skt = accept(para, &addr_other, &addr_other_size); // block it now

		if (new_skt == INVALID_SOCKET) {
			printf("accept return INVALID_SOCKET\n");
			break;
		}

		h1 = _beginthreadex(0, 0, threadTcpRealIO, new_skt, 0, 0);
		assert(h1);
		++i;
		printf("socket handle: %p come in +%d times\n", new_skt, i);
		CloseHandle(h1);
	}
	return 0;
}



int main(int argc, char* argv[]) {
	WSADATA wsData;
	SOCKET skt;

	if (argc != 2) {
		printf("Usage: %s port_number\n", argv[0]);
		return 0;
	}

	if (0 != WSAStartup(MAKEWORD(2,2), &wsData)) {
		assert(0);
	}

	skt = socket(AF_INET, SOCK_STREAM, 0);
	assert(skt != INVALID_SOCKET);

	{
		struct sockaddr_in addr, addr_other;
		int addr_other_size;

		addr.sin_family = AF_INET;
		addr.sin_port = htons(atoi(argv[1]));
		addr.sin_addr.S_un.S_addr = INADDR_ANY;
		if (SOCKET_ERROR == bind(skt, &addr, sizeof(addr))) {
			assert(0);
		}

		printf("SOMAXCONN : %d\n", SOMAXCONN);

		if (SOCKET_ERROR == listen(skt, SOMAXCONN)) {
			assert(0);
		}

		// create listenning thread
		{
			HANDLE h1;

			h1 = _beginthreadex(0, 0, threadTcpListenning, skt, 0, 0);
			assert(h1);

			printf("Press any key to end ...\n");
			getchar();

			closesocket(skt);
			WaitForSingleObject(h1, INFINITE);
			CloseHandle(h1);
		}
	}

	WSACleanup();
	return 0;
}
